import os
from werkzeug.wrappers import Response

from werkzeug.serving import run_simple
from sylfk.wsgi_adapter import wsgi_app
import sylfk.exceptions as exceptions
from sylfk.helper import parse_static_key
from sylfk.route import Route


# 常见异常及其响应对象的映射关系
ERROR_MAP = {
    '401': Response('<h1>401 Unknown or unsupported method.</h1>',
            content_type='text/html; charset=UTF-8', status=401),
    '404': Response('<h1>404 Source Not Found.<h1>',
            content_type='text/html; charset=UTF-8', status=404),
    '503': Response('<h1>503 Unknown function type.</h1>',
            content_type='text/html; charset=UTF-8',  status=503)
}


# 文件类型及其代号的映射关系
TYPE_MAP = {
    'css':  'text/css',
    'js': 'text/js',
    'png': 'image/png',
    'jpg': 'image/jpeg',
    'jpeg': 'image/jpeg'
}


class ExecFunc:
    def __init__(self, func, func_type, **options):
        self.func = func            # 处理函数
        self.options = options      # 附带参数
        self.func_type = func_type  # 函数类型


class SYLFK:
    """应用程序类
    """

    # 实例化方法
    def __init__(self, static_folder='static'):
        self.host = '127.0.0.1'     # 默认主机
        self.port = 8086            # 默认端口
        self.url_map = {}           # 存放 URL 与 Endpoint 的映射
        self.static_map = {}        # 存放 URL 与 静态资源的映射
        self.function_map = {}      # 存放 Endpoint 与请求处理函数的映射
        # 静态资源本地存放路径，默认放在应用所在目录的 static 文件夹下
        self.static_folder = static_folder 
        self.route = Route(self)    # 路由装饰器

    def run(self, host=None, port=None, **options):
        # 如果有参数进来且值不为空，则赋值
        for key, value in options.items():
            if value:
                setattr(self, key, value)
        # 如果 host 不为 None，替换 self.host
        if host:
            self.host = host
        # 如果 port 不为 None，替换 self.port
        if port:
            self.port = port
        # 映射静态资源处理函数，所有静态资源处理函数都是静态资源路由
        self.function_map['static'] = ExecFunc(func=self.dispatch_static, 
                func_type='static')
        # 把框架本身也就是应用本身和其它几个配置参数传给 werkzeug 的 run_simple
        run_simple(hostname=self.host, port=self.port, application=self, **options)

    def dispatch_static(self, static_path):
        """处理静态资源相关请求，返回响应对象
        """
        # 如果静态文件路径不存在，返回 404 异常响应对象
        if not os.path.exists(static_path):
            return ERROR_MAP['404']

        key = parse_static_key(static_path)             # 文件名
        file_type = TYPE_MAP.get(key, 'text/plain')     # 文件类型

        with open(static_path, 'rb') as f:
            data = f.read()
        return Response(data, content_type=file_type)

    def dispatch_request(self, request, status=200):
        """处理请求，返回响应对象
        """
        # 定义响应报头，Server 字段的值表示运行的服务名
        # 通常有 IIS， Apache，Tomcat，Nginx 等，这里自定义为 Haha Web 0.1
        headers = {'Server': 'Haha Web 0.1'}

        # 从 URL 中提取路径
        url = request.base_url.replace(request.host_url, '/')

        # 如果路径以静态资源目录开头，则请求的是静态资源，节点为 'static'
        if url.startswith('/' + self.static_folder + '/'):
            endpoint = 'static'
            url = url[1:]
        else:
            # 从映射表中获取节点
            endpoint = self.url_map.get(url, None)

        # 如果节点为空，返回 404
        if endpoint is None:
            return ERROR_MAP['404']

        # 获取节点对应的执行函数
        exec_function = self.function_map[endpoint]

        # 判断执行函数类型作进一步处理
        if exec_function.func_type == 'route':
            # 判断请求方法是否支持
            if request.method in exec_function.options.get('methods'):
                # 判断视图函数的执行是否需要请求对象 request 参与
                # argcount 的值是视图函数的位置参数 + 默认参数的数量之和
                argcount = exec_function.func.__code__.co_argcount
                if argcount > 0:
                    # 需要附带请求体进行结果处理
                    rep = exec_function.func(request)
                else:
                    # 不需要附带请求体进行结果处理
                    rep = exec_function.func()
            else:
                # 返回 401 错误响应体
                return ERROR_MAP['401']

        elif exec_function.func_type == 'view':
            # 所有视图处理函数都需要附带请求体来获取处理结果
            rep = exec_function.func(request)

        elif exec_function.func_type == 'static':
            # 静态资源返回的是一个预先封装好的响应体，所以直接返回
            return exec_function.func(url)

        else:
            # 返回 503 错误响应体
            return ERROR_MAP['503']

        # 定义响应体类型
        content_type = 'text/html; charset=UTF-8'

        # 返回响应体
        return Response(rep, content_type=content_type, headers=headers, 
                status=status)
    
    # 框架被 WSGI 调用入口的方法
    def __call__(self, environ, start_response):
        return wsgi_app(self, environ, start_response)

    # 添加路由规则
    def add_url_rule(self, url, func, func_type, endpoint=None, **options):

        # 如果节点未命名，使用处理函数的名字
        if endpoint is None:
            endpoint = func.__name__

        # 抛出 URL 已存在异常
        if url in self.url_map:
            raise exceptions.URLExistsError

        # 如果类型不是静态资源，并且节点已存在，则抛出节点已存在异常
        if endpoint in self.function_map and func_type != 'static':
            raise exceptions.EndpointExistsError

        # 添加 URL 与节点映射
        self.url_map[url] = endpoint

        # 添加节点与请求处理函数映射
        self.function_map[endpoint] = ExecFunc(func, func_type, **options)
